package com.scs.application.core.model;

import org.apache.commons.beanutils.BeanUtils;
import com.google.common.collect.Lists;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 用于将list输出成树形结构
 * @param <T>
 */
public class TreeModel<T> implements Serializable {

    private List<T> data;

    private String idFieldName;

    private String labelFieldName;

    private String parentFieldName;

    private String sortFieldName;

    private List<TreeNode> nodes;

    private static final String DEFAULT_SORT_FIELD_NAME = "sort";
    private static final String DEFAULT_ID_FIELD_NAME = "id";
    private static final String DEFAULT_PARENT_FIELD_NAME = "parentId";

    static {
        // 避免  java.lang.IllegalArgumentException: Comparison method violates its general contract! 异常
        System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
    }

    /**
     * 默认排序，根据对象的sort属性进行排序
     */
    private final Comparator defaultComparator = (Comparator<T>) (o1, o2) -> {
        try {
            String value1 = BeanUtils.getProperty(o1, sortFieldName);
            if(!StringUtils.isNumeric(value1)){
                value1 = "99999";
            }

            String value2 = BeanUtils.getProperty(o2, sortFieldName);
            if(!StringUtils.isNumeric(value2)){
                value2 = "99999";
            }

            Integer sort1 = Integer.parseInt(StringUtils.defaultIfBlank(value1, "0"));
            Integer sort2 = Integer.parseInt(StringUtils.defaultIfBlank(value2, "0"));
            return sort1 - sort2;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    };


    /**
     * 数据分组之后存储的数据
     */
    private Map<String, List<T>> groupingByMap = null;


    public TreeModel(List<T> data, String labelFieldName) {
        this(data, DEFAULT_ID_FIELD_NAME, labelFieldName, DEFAULT_PARENT_FIELD_NAME, DEFAULT_SORT_FIELD_NAME, null);
    }


    public TreeModel(List<T> data, String labelFieldName, TreeNode root) {
        this(data, DEFAULT_ID_FIELD_NAME, labelFieldName, DEFAULT_PARENT_FIELD_NAME, DEFAULT_SORT_FIELD_NAME, root);
    }

    public TreeModel(List<T> data, String idFieldName, String labelFieldName, String parentFieldName, TreeNode root) {
        this(data, idFieldName, labelFieldName, parentFieldName, DEFAULT_SORT_FIELD_NAME, root);
    }

    public TreeModel(List<T> data, String idFieldName, String labelFieldName, String parentFieldName, String sortFieldName, TreeNode root) {
        this.data = data;
        this.idFieldName = idFieldName;
        this.labelFieldName = labelFieldName;
        this.parentFieldName = parentFieldName;
        this.sortFieldName = sortFieldName;
        this.nodes = root == null ? null : Lists.newArrayList(root);
    }

    public List<TreeNode> asList() throws RuntimeException {
        if (CollectionUtils.isEmpty(data)) {
            return Collections.EMPTY_LIST;
        }


        final String parentFieldName = this.parentFieldName;
        try {

            // 对数据进行排序，再分组
            groupingByMap = data.stream().sorted(getComparator()).collect(Collectors.groupingBy(o -> {
                try {
                    return (StringUtils.defaultString(BeanUtils.getProperty(o, parentFieldName), "0"));
                } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                    e.printStackTrace();
                    return null;
                }
            }));

            Map<String, List<T>> sortMap = new TreeMap<>(Comparator.naturalOrder());
            sortMap.putAll(groupingByMap);

            groupingByMap = sortMap;

            //   根节点
            if (CollectionUtils.isEmpty(nodes)) {
                // 如果构造树形对象的时候未指定root，循环添加根节点
                List<T> ts = groupingByMap.values().iterator().next();
                nodes = Lists.newLinkedList();
                TreeNode node;
                for (T t : ts) {
                    node = buildNode(t);
                    nodes.add(node);
                    transfer(node, groupingByMap.get(node.getId()));
                }
            } else {
                transfer(nodes.get(0), groupingByMap.values().iterator().next());
            }
        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            throw new RuntimeException("树形结构转换失败", e);
        }
        return nodes;
    }

    private void transfer(TreeNode parent, List<T> children) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        if (CollectionUtils.isEmpty(children)) {
            parent.setLeaf(true);
            return;
        }
        // 对子节点进行排序
        children = children.stream().sorted(getComparator()).collect(Collectors.toList());

        List<TreeNode> children1 = parent.getChildren();
        if (CollectionUtils.isEmpty(children1)) {
            children1 = Lists.newLinkedList();
        }


        for (T t : children) {
            TreeNode node = buildNode(t);
            children1.add(node);
            transfer(node, groupingByMap.get(node.getId()));
        }
        parent.setChildren(children1);
    }

    private TreeNode buildNode(T entity) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        TreeNode node = new TreeNode();
        String id = BeanUtils.getProperty(entity, idFieldName);

        if (StringUtils.isBlank(id)) {
            throw new RuntimeException("转换错误，请检查数据是否正确!");
        }

        node.setId(id);
        node.setTitle(BeanUtils.getProperty(entity, labelFieldName));
        node.setLeaf(false);
        node.setParentId(BeanUtils.getProperty(entity, parentFieldName));
        addExtraProperties(node, entity);
        return node;
    }

    /**
     * 为节点添加自定义属性
     *
     * @param node
     * @param entity
     */
    protected void addExtraProperties(final TreeNode node, final T entity) {

    }

    /**
     * 当转换成树形结构排序时使用，如果需要自定义排序规则，请覆盖此方法。此方法默认根据对象的{@link #DEFAULT_SORT_FIELD_NAME}属性升序排列
     *
     * @return
     */
    protected Comparator<? super T> getComparator() {
        return defaultComparator;
    }
}

